package beautiful.butterfly.drds.schedule.zoo_keeper;

import beautiful.butterfly.drds.schedule.strategy.ManagerFactoryInfo;
import beautiful.butterfly.drds.schedule.strategy.RunntimeStrategy;
import beautiful.butterfly.drds.schedule.strategy.Strategy;
import beautiful.butterfly.drds.schedule.strategy.factory.ManageFactory;
import beautiful.butterfly.drds.schedule.zoo_keeper.data_manager.TimestampTypeAdapter;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;

import java.io.Writer;
import java.sql.Timestamp;
import java.util.*;

public class StrategyDataManager {

    private ZooKeeperManager zooKeeperManager;
    private String PATH_Strategy;
    private String PATH_ManagerFactory;
    private Gson gson;

    //��Spring���󴴽���Ϻ󣬴����ڲ�����
    public StrategyDataManager(ZooKeeperManager zooKeeperManager) throws Exception {
        this.zooKeeperManager = zooKeeperManager;
        gson = new GsonBuilder().registerTypeAdapter(Timestamp.class, new TimestampTypeAdapter()).setDateFormat("yyyy-MM-dd HH:mm:ss").create();
        this.PATH_Strategy = this.zooKeeperManager.getRootPath() + "/strategy";
        this.PATH_ManagerFactory = this.zooKeeperManager.getRootPath() + "/factory";

        if (this.getZooKeeper().exists(this.PATH_Strategy, false) == null) {
            ZooKeeperTools.createPath(getZooKeeper(), this.PATH_Strategy, CreateMode.PERSISTENT, this.zooKeeperManager.getAclList());
        }
        if (this.getZooKeeper().exists(this.PATH_ManagerFactory, false) == null) {
            ZooKeeperTools.createPath(getZooKeeper(), this.PATH_ManagerFactory, CreateMode.PERSISTENT, this.zooKeeperManager.getAclList());
        }
    }

    public Strategy loadStrategy(String strategyName)
            throws Exception {
        String zkPath = this.PATH_Strategy + "/" + strategyName;
        if (this.getZooKeeper().exists(zkPath, false) == null) {
            return null;
        }
        String valueString = new String(this.getZooKeeper().getData(zkPath, false, null));
        Strategy result = (Strategy) this.gson.fromJson(valueString, Strategy.class);
        return result;
    }

    public void createScheduleStrategy(Strategy strategy) throws Exception {
        String zkPath = this.PATH_Strategy + "/" + strategy.getStrategyName();
        String valueString = this.gson.toJson(strategy);
        if (this.getZooKeeper().exists(zkPath, false) == null) {
            this.getZooKeeper().create(zkPath, valueString.getBytes(), this.zooKeeperManager.getAclList(), CreateMode.PERSISTENT);
        } else {
            throw new Exception("���Ȳ���" + strategy.getStrategyName() + "�Ѿ�����,���ȷ����Ҫ�ؽ������ȵ���deleteMachineStrategy(String taskType)ɾ��");
        }
    }

    public void updateScheduleStrategy(Strategy strategy)
            throws Exception {
        String zkPath = this.PATH_Strategy + "/" + strategy.getStrategyName();
        String valueString = this.gson.toJson(strategy);
        if (this.getZooKeeper().exists(zkPath, false) == null) {
            this.getZooKeeper().create(zkPath, valueString.getBytes(), this.zooKeeperManager.getAclList(), CreateMode.PERSISTENT);
        } else {
            this.getZooKeeper().setData(zkPath, valueString.getBytes(), -1);
        }

    }

    public void deleteMachineStrategy(String taskType) throws Exception {
        deleteMachineStrategy(taskType, false);
    }

    public void pause(String strategyName) throws Exception {
        Strategy strategy = this.loadStrategy(strategyName);
        strategy.setSts(Strategy.STS_PAUSE);
        this.updateScheduleStrategy(strategy);
    }

    public void resume(String strategyName) throws Exception {
        Strategy strategy = this.loadStrategy(strategyName);
        strategy.setSts(Strategy.STS_RESUME);
        this.updateScheduleStrategy(strategy);
    }

    public void deleteMachineStrategy(String taskType, boolean isForce) throws Exception {
        String zkPath = this.PATH_Strategy + "/" + taskType;
        if (isForce == false && this.getZooKeeper().getChildren(zkPath, null).size() > 0) {
            throw new Exception("����ɾ��" + taskType + "�����в��ԣ��ᵼ�±�����������Ӧ�ò���ֹͣʧȥ���Ƶĵ��Ƚ��̡�" +
                    "���������IP��ַ�������еĵ�������ֹͣ����ɾ�����Ȳ���");
        }
        ZooKeeperTools.deleteTree(this.getZooKeeper(), zkPath);
    }

    public List<Strategy> loadAllScheduleStrategy() throws Exception {
        String zkPath = this.PATH_Strategy;
        List<Strategy> result = new ArrayList<Strategy>();
        List<String> names = this.getZooKeeper().getChildren(zkPath, false);
        Collections.sort(names);
        for (String name : names) {
            result.add(this.loadStrategy(name));
        }
        return result;
    }

    /**
     * ע��ManagerFactory
     *
     * @param manageFactory
     * @return ��Ҫȫ��ע���ĵ��ȣ����統IP�����б���
     * @throws Exception
     */
    public List<String> registerManagerFactory(ManageFactory manageFactory) throws Exception {

        if (manageFactory.getUuid() == null) {
            String uuid = manageFactory.getIp() + "$" + manageFactory.getHostName() + "$" + UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
            String zkPath = this.PATH_ManagerFactory + "/" + uuid + "$";
            zkPath = this.getZooKeeper().create(zkPath, null, this.zooKeeperManager.getAclList(), CreateMode.EPHEMERAL_SEQUENTIAL);
            manageFactory.setUuid(zkPath.substring(zkPath.lastIndexOf("/") + 1));
        } else {
            String zkPath = this.PATH_ManagerFactory + "/" + manageFactory.getUuid();
            if (this.getZooKeeper().exists(zkPath, false) == null) {
                zkPath = this.getZooKeeper().create(zkPath, null, this.zooKeeperManager.getAclList(), CreateMode.EPHEMERAL);
            }
        }

        List<String> result = new ArrayList<String>();
        for (Strategy strategy : loadAllScheduleStrategy()) {
            boolean isFind = false;
            //��ͣ���߲���IP��Χ
            if (Strategy.STS_PAUSE.equalsIgnoreCase(strategy.getSts()) == false && strategy.getIPList() != null) {
                for (String ip : strategy.getIPList()) {
                    if (ip.equals("127.0.0.1") || ip.equalsIgnoreCase("localhost") || ip.equals(manageFactory.getIp()) || ip.equalsIgnoreCase(manageFactory.getHostName())) {
                        //��ӿɹ���TaskType
                        String zkPath = this.PATH_Strategy + "/" + strategy.getStrategyName() + "/" + manageFactory.getUuid();
                        if (this.getZooKeeper().exists(zkPath, false) == null) {
                            zkPath = this.getZooKeeper().create(zkPath, null, this.zooKeeperManager.getAclList(), CreateMode.EPHEMERAL);
                        }
                        isFind = true;
                        break;
                    }
                }
            }
            if (isFind == false) {//���ԭ��ע���Factory
                String zkPath = this.PATH_Strategy + "/" + strategy.getStrategyName() + "/" + manageFactory.getUuid();
                if (this.getZooKeeper().exists(zkPath, false) != null) {
                    ZooKeeperTools.deleteTree(this.getZooKeeper(), zkPath);
                    result.add(strategy.getStrategyName());
                }
            }
        }
        return result;
    }

    /**
     * ע������ֹͣ����
     *
     * @param manageFactory
     * @return
     * @throws Exception
     */
    public void unRregisterManagerFactory(ManageFactory manageFactory) throws Exception {
        for (String taskName : this.getZooKeeper().getChildren(this.PATH_Strategy, false)) {
            String zkPath = this.PATH_Strategy + "/" + taskName + "/" + manageFactory.getUuid();
            if (this.getZooKeeper().exists(zkPath, false) != null) {
                ZooKeeperTools.deleteTree(this.getZooKeeper(), zkPath);
            }
        }
    }

    public RunntimeStrategy loadScheduleStrategyRunntime(String strategyName, String uuid) throws Exception {
        String zkPath = this.PATH_Strategy + "/" + strategyName + "/" + uuid;
        RunntimeStrategy result = null;
        if (this.getZooKeeper().exists(zkPath, false) != null) {
            byte[] value = this.getZooKeeper().getData(zkPath, false, null);
            if (value != null) {
                String valueString = new String(value);
                result = (RunntimeStrategy) this.gson.fromJson(valueString, RunntimeStrategy.class);
                if (null == result) {
                    throw new Exception("gson �����л��쳣,����Ϊnull");
                }
                if (null == result.getStrategyName()) {
                    throw new Exception("gson �����л��쳣,��������Ϊnull");
                }
                if (null == result.getUuid()) {
                    throw new Exception("gson �����л��쳣,uuidΪnull");
                }
            } else {
                result = new RunntimeStrategy();
                result.setStrategyName(strategyName);
                result.setUuid(uuid);
                result.setRequestNum(0);
                result.setMessage("");
            }
        }
        return result;
    }

    /**
     * װ�����еĲ�������״̬
     *
     * @return
     * @throws Exception
     */
    public List<RunntimeStrategy> loadAllScheduleStrategyRunntime() throws Exception {
        List<RunntimeStrategy> result = new ArrayList<RunntimeStrategy>();
        String zkPath = this.PATH_Strategy;
        for (String taskType : this.getZooKeeper().getChildren(zkPath, false)) {
            for (String uuid : this.getZooKeeper().getChildren(zkPath + "/" + taskType, false)) {
                result.add(loadScheduleStrategyRunntime(taskType, uuid));
            }
        }
        return result;
    }

    public List<RunntimeStrategy> loadAllScheduleStrategyRunntimeByUUID(String managerFactoryUUID) throws Exception {
        List<RunntimeStrategy> result = new ArrayList<RunntimeStrategy>();
        String zkPath = this.PATH_Strategy;

        List<String> taskTypeList = this.getZooKeeper().getChildren(zkPath, false);
        Collections.sort(taskTypeList);
        for (String taskType : taskTypeList) {
            if (this.getZooKeeper().exists(zkPath + "/" + taskType + "/" + managerFactoryUUID, false) != null) {
                result.add(loadScheduleStrategyRunntime(taskType, managerFactoryUUID));
            }
        }
        return result;
    }

    public List<RunntimeStrategy> loadAllScheduleStrategyRunntimeByTaskType(String strategyName) throws Exception {
        List<RunntimeStrategy> result = new ArrayList<RunntimeStrategy>();
        String zkPath = this.PATH_Strategy;
        if (this.getZooKeeper().exists(zkPath + "/" + strategyName, false) == null) {
            return result;
        }
        List<String> uuidList = this.getZooKeeper().getChildren(zkPath + "/" + strategyName, false);
        //����
        Collections.sort(uuidList, new Comparator<String>() {
            public int compare(String u1, String u2) {
                return u1.substring(u1.lastIndexOf("$") + 1).compareTo(
                        u2.substring(u2.lastIndexOf("$") + 1));
            }
        });

        for (String uuid : uuidList) {
            result.add(loadScheduleStrategyRunntime(strategyName, uuid));
        }
        return result;
    }

    /**
     * ������������
     *
     * @param manangerFactoryUUID
     * @param requestNum
     * @throws KeeperException
     * @throws InterruptedException
     */
    public void updateStrategyRunntimeReqestNum(String strategyName, String manangerFactoryUUID, int requestNum) throws Exception {
        String zkPath = this.PATH_Strategy + "/" + strategyName + "/" + manangerFactoryUUID;
        RunntimeStrategy result = null;
        if (this.getZooKeeper().exists(zkPath, false) != null) {
            result = this.loadScheduleStrategyRunntime(strategyName, manangerFactoryUUID);
        } else {
            result = new RunntimeStrategy();
            result.setStrategyName(strategyName);
            result.setUuid(manangerFactoryUUID);
            result.setRequestNum(requestNum);
            result.setMessage("");
        }
        result.setRequestNum(requestNum);
        String valueString = this.gson.toJson(result);
        this.getZooKeeper().setData(zkPath, valueString.getBytes(), -1);
    }

    /**
     * ���µ��ȹ����е���Ϣ
     *
     * @param strategyName
     * @param manangerFactoryUUID
     * @param message
     * @throws Exception
     */
    public void updateStrategyRunntimeErrorMessage(String strategyName, String manangerFactoryUUID, String message) throws Exception {
        String zkPath = this.PATH_Strategy + "/" + strategyName + "/" + manangerFactoryUUID;
        RunntimeStrategy result = null;
        if (this.getZooKeeper().exists(zkPath, false) != null) {
            result = this.loadScheduleStrategyRunntime(strategyName, manangerFactoryUUID);
        } else {
            result = new RunntimeStrategy();
            result.setStrategyName(strategyName);
            result.setUuid(manangerFactoryUUID);
            result.setRequestNum(0);
        }
        result.setMessage(message);
        String valueString = this.gson.toJson(result);
        this.getZooKeeper().setData(zkPath, valueString.getBytes(), -1);
    }

    public void updateManagerFactoryInfo(String uuid, boolean isStart) throws Exception {
        String zkPath = this.PATH_ManagerFactory + "/" + uuid;
        if (this.getZooKeeper().exists(zkPath, false) == null) {
            throw new Exception("���������������:" + uuid);
        }
        this.getZooKeeper().setData(zkPath, Boolean.toString(isStart).getBytes(), -1);
    }

    public ManagerFactoryInfo loadManagerFactoryInfo(String uuid) throws Exception {
        String zkPath = this.PATH_ManagerFactory + "/" + uuid;
        if (this.getZooKeeper().exists(zkPath, false) == null) {
            throw new Exception("���������������:" + uuid);
        }
        byte[] value = this.getZooKeeper().getData(zkPath, false, null);
        ManagerFactoryInfo result = new ManagerFactoryInfo();
        result.setUuid(uuid);
        if (value == null) {
            result.setStart(true);
        } else {
            result.setStart(Boolean.parseBoolean(new String(value)));
        }
        return result;
    }

    /**
     * ����������Ϣ��Ŀǰ֧��baseTaskType��strategy���ݡ�
     *
     * @param config
     * @param writer
     * @param isUpdate
     * @throws Exception
     */
    public void importConfig(String config, Writer writer, boolean isUpdate)
            throws Exception {
        ConfigNode configNode = gson.fromJson(config, ConfigNode.class);
        if (configNode != null) {
            String path = configNode.getRootPath() + "/"
                    + configNode.getConfigType();
            ZooKeeperTools.createPath(getZooKeeper(), path, CreateMode.PERSISTENT, zooKeeperManager.getAclList());
            String y_node = path + "/" + configNode.getName();
            if (getZooKeeper().exists(y_node, false) == null) {
                writer.append("<font color=\"red\">�ɹ�������������Ϣ\n</font>");
                getZooKeeper().create(y_node, configNode.getValue().getBytes(),
                        zooKeeperManager.getAclList(), CreateMode.PERSISTENT);
            } else if (isUpdate) {
                writer.append("<font color=\"red\">��������Ϣ�Ѿ����ڣ�����ǿ�Ƹ�����\n</font>");
                getZooKeeper().setData(y_node,
                        configNode.getValue().getBytes(), -1);
            } else {
                writer.append("<font color=\"red\">��������Ϣ�Ѿ����ڣ������Ҫ���£�������ǿ�Ƹ���\n</font>");
            }
        }
        writer.append(configNode.toString());
    }

    /**
     * ���������Ϣ��Ŀǰ����baseTaskType��strategy���ݡ�
     *
     * @param rootPath
     * @param writer
     * @throws Exception
     */
    public StringBuffer exportConfig(String rootPath, Writer writer)
            throws Exception {
        StringBuffer buffer = new StringBuffer();
        for (String type : new String[]{"baseTaskType", "strategy"}) {
            if (type.equals("baseTaskType")) {
                writer.write("<h2>�������������б�</h2>\n");
            } else {
                writer.write("<h2>�������������б�</h2>\n");
            }
            String bTTypePath = rootPath + "/" + type;
            List<String> fNodeList = getZooKeeper().getChildren(bTTypePath,
                    false);
            for (int i = 0; i < fNodeList.size(); i++) {
                String fNode = fNodeList.get(i);
                ConfigNode configNode = new ConfigNode(rootPath, type, fNode);
                configNode.setValue(new String(this.getZooKeeper().getData(bTTypePath + "/" + fNode, false, null)));
                buffer.append(gson.toJson(configNode));
                buffer.append("\n");
                writer.write(configNode.toString());
            }
            writer.write("\n\n");
        }
        if (buffer.length() > 0) {
            String str = buffer.toString();
            return new StringBuffer(str.substring(0, str.length() - 1));
        }
        return buffer;
    }

    public List<ManagerFactoryInfo> loadAllManagerFactoryInfo() throws Exception {
        String zkPath = this.PATH_ManagerFactory;
        List<ManagerFactoryInfo> result = new ArrayList<ManagerFactoryInfo>();
        List<String> names = this.getZooKeeper().getChildren(zkPath, false);
        Collections.sort(names, new Comparator<String>() {
            public int compare(String u1, String u2) {
                return u1.substring(u1.lastIndexOf("$") + 1).compareTo(
                        u2.substring(u2.lastIndexOf("$") + 1));
            }
        });
        for (String name : names) {
            ManagerFactoryInfo info = new ManagerFactoryInfo();
            info.setUuid(name);
            byte[] value = this.getZooKeeper().getData(zkPath + "/" + name, false, null);
            if (value == null) {
                info.setStart(true);
            } else {
                info.setStart(Boolean.parseBoolean(new String(value)));
            }
            result.add(info);
        }
        return result;
    }

    public void printTree(String path, Writer writer, String lineSplitChar)
            throws Exception {
        ZooKeeperTools.printTree(this.getZooKeeper(), path, writer, lineSplitChar);
    }

    public void deleteTree(String path) throws Exception {
        ZooKeeperTools.deleteTree(this.getZooKeeper(), path);
    }

    public ZooKeeper getZooKeeper() throws Exception {
        return this.zooKeeperManager.getZooKeeper();
    }

    public String getRootPath() {
        return this.zooKeeperManager.getRootPath();
    }
}
